(* -*- sml -*- *)
(**
 * syntax for the interface.
 *
 * @copyright (C) 2021 SML# Development Team.
 * @author UENO Katsuhiro
 * @author Atsushi Ohori
 * @author Liu Bochao
 *)
structure AbsynInterface =
struct

  (*% *)
  datatype constraint 
    = (*% @format ":" *)
      SIG_TRANSPARENT
    | (*% @format ":>" *)
      SIG_OPAQUE

  (*% @formatter(Absyn.ty) AbsynFormatter.format_ty *)
  type ty = Absyn.ty

  (*% @formatter(Symbol.symbol) Symbol.format_symbol*)
  type symbol = Symbol.symbol

  (*% @formatter(Symbol.longsymbol) Symbol.format_longsymbol*)
  type longsymbol = Symbol.longsymbol

  (*% @formatter(Loc.loc) Loc.format_loc *)
  type loc = Loc.loc

  (*%
   * @formatter(Absyn.tvar) AbsynFormatter.format_tvar
   *)
  datatype overloadInstance 
    = (*% @format(exp) exp *)
      INST_OVERLOAD of overloadCase
    | (*% @format({longsymbol}) longsymbol *)
      INST_LONGVID of {longsymbol: longsymbol}

  withtype overloadCase 
    = (*%
       * @format({tyvar, expTy, matches: m ms, loc})
       * "case" +d tyvar +d "in" +d expTy +d "of" +1 ms(m)(+1 "|" +d)
       * @format:m({instTy, instance})
       * instTy +d "=>" 2[ +1 instance ]
       *)
      {tyvar: Absyn.tvar,
       expTy: ty,
       matches: {instTy: ty, instance: overloadInstance} list,
       loc: loc}

  (*% @params(symbol) *)
  datatype valbindBody 
    = (*%
         @format({ty})
           1[ symbol + ":" +1 ty]
       *)
      VAL_EXTERN of {ty: ty}
    | (*%
         @format(longsymbol)
           1[ symbol + "=" + symbol]
       *)
      (* 2012-1-4 ohori: variable alias in interface added *)
      VALALIAS_EXTERN of longsymbol
    | (*%
       * @format({builtinSymbol, ty})
       * R0{ symbol +1 "=" +1 L2{ "_builtin" +d builtinSymbol +1 ":" +d ty }}
       *)
      VAL_BUILTIN of {builtinSymbol: symbol, ty: ty}
    | (*%
       * @format(cases)
       * R0{ symbol +d "=" +1 cases }
       *)
      VAL_OVERLOAD of overloadCase

  (*% *)
  type valbind 
    = (*%
       * @format({symbol, body, loc}) body()(symbol)
       *)
      {symbol: symbol, body: valbindBody, loc: loc}

  (*%
   * @formatter(list) TermFormat.formatSeqList
   * @formatter(Absyn.tvar) AbsynFormatter.format_tvar
   *)
  type typbind_trans
    = (*%
       * @format({tyvars: tv tvs, symbol, ty, loc})
       * tvs(tv)("(","," 1,")" +d) symbol +d "="
       * 2[ +1 ty]
       *)
      {tyvars: Absyn.tvar list, symbol: symbol, ty: ty, loc: loc}

  (*% @formatter(AbsynTy.opaque_impl) AbsynTyFormatter.format_opaque_impl *)
  datatype opaque_impl = datatype AbsynTy.opaque_impl

  (*%
   * @formatter(list) TermFormat.formatSeqList
   * @formatter(Absyn.tvar) AbsynFormatter.format_tvar
   * @formatter(bool) SmlppgUtil.formatBinaryChoice
   *)
  datatype typbind 
    = (*% @format(t) t *)
      TRANSPARENT of typbind_trans
    | (*%
       * @format({eq, tyvars: tv tvs, symbol, runtimeTy, loc})
       * tvs(tv)("(","," 1,")" +d) symbol +d "="
       * 2[ +1 runtimeTy +1 "as" +d eq()("opaqueEq","opaqueNoneq") ]
       *)
      OPAQUE of {eq: bool, tyvars: Absyn.tvar list, symbol: symbol,
                 runtimeTy: opaque_impl, loc: loc}

  (*%
   * @formatter(option) TermFormat.formatOptionalOption
   *)
  type conbind 
    = (*%
       * @format({symbol, ty: ty tyopt, loc})
       * R1{ symbol +d 2[ tyopt(ty)("of" +1,) ] }
       *)
      {symbol: symbol, ty: ty option, loc:loc}

  (*%
   * @formatter(seq) TermFormat.formatSeqList
   * @formatter(ifCons) TermFormat.formatIfCons
   * @formatter(Absyn.tvar) AbsynFormatter.format_tvar
   *)
  type datbind 
    = (*%
         @format({tyvars: tv tvs, symbol, conbind: bind binds, loc})
         1[
           tvs:seq(tv)("(",",",")") 
           tvs:ifCons()(+)
           symbol +d "="
           +1 
           binds(bind)(+1 "|" +d)
         ]
       *)
      {tyvars: Absyn.tvar list, symbol: symbol, conbind: conbind list, loc:loc}

  (*%
   * @formatter(option) TermFormat.formatOptionalOption
   *)
  datatype exbind 
    = (*%
       * @format({symbol, ty: ty tyopt, loc})
       * R1{ symbol +d 2[ tyopt(ty)("of" +1,) ] }
       *)
      EXNDEF of {symbol: symbol, ty: ty option, loc: loc}
    | (*%
       * @format({symbol, longsymbol, loc})
       * R1{ symbol +d 2[ "=" +d longsymbol ] }
       *)
      EXNREP of {symbol: symbol, longsymbol: longsymbol, loc: loc}

  (*%
   * @formatter(declist) TermFormat.formatDeclList
   * @formatter(Absyn.sigexp) AbsynFormatter.format_sigexp
   *)
  datatype idec 
    = (*%
       * @format(bind)
       * "val" +d bind
       *)
      IVAL of valbind
    | (*%
       * @format(bind binds)
       * "type" +d binds(bind)(+1 "and" +d)
       *)
      ITYPE of typbind list
    | (*%
         @format({datbind: bind binds, withType: typ typs, loc})
         1[
           "datatype" +d binds(bind)(~1[1 "and"] +d)
           "withtype" +d typs(typ)(~1[1 "and"] +d)
          ]
       *)
      IDATATYPE of {datbind: datbind list, withType: typbind_trans list,
                    loc: loc}
    | (*%
       * @format({symbol, longsymbol,  loc})
       * "datatype" +d symbol +d "="
       * 2[ +1 "datatype" +d  longsymbol ]
       *)
      ITYPEREP of {symbol: symbol, longsymbol: longsymbol, loc: loc}
    | (*%
       * @format({symbol, builtinSymbol, loc})
       * "datatype" +d symbol +d "="
       * 2[ +1 "_builtin" +d "datatype" +d builtinSymbol]
       *)
      ITYPEBUILTIN of {symbol: symbol, builtinSymbol: symbol,loc: loc}
    | (*%
       * @format(bind binds)
       * "exception" +d binds(bind)(+1 "and" +d)
       *)
      IEXCEPTION of exbind list
    | (*%
         @format(bind)
           bind
       *)
      ISTRUCTURE of strbind

  and istrexp 
    = (*%
          @format({decs: dec decs, loc})
            "struct"
               1[ decs:declist(dec)(+1,+1) ]
            +1
            "end"
       *)
      ISTRUCT of {decs: idec list, loc: loc}
    | (*%
       * @format({longsymbol, loc})  longsymbol
       *)
      ISTRUCTREP of {longsymbol:longsymbol, loc: loc}
    | (*%
       * @format({functorSymbol, argument, loc})  functorSymbol "(" argument ")"
       *)
      IFUNCTORAPP of {functorSymbol:symbol, argument:longsymbol, loc: loc}

  withtype strbind 
    = (*%
          @format({symbol, strexp, loc})
            1[ 
              "structure" + symbol  +d "=" 
              +1 strexp 
            ]
       *)
      {
        symbol: symbol,
        strexp: istrexp,
        loc: loc
      }

  (*%
   * @formatter(Absyn.sigexp) AbsynFormatter.format_sigexp
   *)
  type sigbind 
    = (*%
         @format({symbol, sigexp, loc})
          1[ symbol +d "=" +1 sigexp]
       *)
      {symbol: symbol, sigexp: Absyn.sigexp, loc: loc}

  (*%
   * @formatter(Absyn.sigexp) AbsynFormatter.format_sigexp
   * @formatter(Absyn.spec) AbsynFormatter.format_spec
   *)
  datatype funParam 
    = (*%
         @format({symbol, sigexp})
           symbol + ":" 
           +1
           sigexp
       *)
      FUNPARAM_FULL of {symbol: symbol, sigexp: Absyn.sigexp}
    | (*%
       * @format(spec) spec
       *)
      FUNPARAM_SPEC of Absyn.spec

  (*%
   * @formatter(Absyn.sigexp) AbsynFormatter.format_sigexp
   *)
  type funbind 
    = (*%
         @format({functorSymbol, param, strexp, loc})
           1[ 
             "functor" + functorSymbol
             +1 "(" 1[param] ")" +d "=" 
             +1 strexp 
           ]
       *)
      {
        functorSymbol: symbol,
        param: funParam,
        strexp: istrexp,
        loc: loc
      }

  (*%
   * @formatter(IntInf.int) TermFormat.format_IntInf_dec_ML
   * @formatter(option) TermFormat.formatOptionalOption
   *)
  datatype fixity 
    = (*% @format(x xo) "infix" xo(x)(+d,) *)
      INFIXL of string option
    | (*% @format(x xo) "infixr" xo(x)(+d,) *)
      INFIXR of string option
    | (*% @format "nonfix" *)
      NONFIX

  (*% *)
  datatype itopdec 
    = (*% @format(x) x *)
      IDEC of idec
    | (*%
        @format(bind)
          bind
       *)
      IFUNDEC of funbind
    | (*%
         @format({fixity, symbols: symbol symbols, loc})
           1[ fixity +d symbols(symbol)(+d) ]
       *)
      IINFIX of {fixity: fixity, symbols: symbol list, loc: loc}

  (*%
   * @formatter(RequirePath.path) RequirePath.format_path
   * @formatter(Symbol.symbol) Symbol.format_symbol
   *)
  datatype irequire =
      (*%
       * @format(s * opt opts * l) "_require" +d s opts(opt)()
       * @format:opt(opt) +d opt
       *)
      REQUIRE of RequirePath.path * Symbol.symbol list * loc
    | (*%
       * @format(s * opt opts * l) "local" +d "_require" +d s opts(opt)()
       * @format:opt(opt) +d opt
       *)
      LOCAL_REQUIRE of RequirePath.path * Symbol.symbol list * loc
    | (*% @format(s * l) "local" +d "_use" +d s *)
      LOCAL_USE of RequirePath.path * loc

  (*%
   * @formatter(Absyn.topdec) AbsynFormatter.format_topdec
   * @formatter(dec) TermFormat.formatDeclList
   * @formatter(RequirePath.path) RequirePath.format_path
   *)
  datatype itop 
    = (*%
       * @format({requires: req reqs, provide: x xs})
       * reqs:dec(req)(+1,+1) xs(x)(+1)
       *)
      INTERFACE of
      {
        requires: irequire list,
        provide: itopdec list
      }
    | (*%
       * @format({includes: n ns, topdecs: x xs})
       * ns:dec(n)(+1 "include" +d,+1 "include" +d)
       * xs(x)(+1)
       * @format:n(f * l) f
       *)
      INCLUDES of
      {
        includes: (RequirePath.path * loc) list,
        topdecs: Absyn.topdec list
      }

  (*%
   * @formatter(InterfaceID.id) InterfaceID.format_id
   * @formatter(InterfaceName.interface_name)
   *    InterfaceName.format_interface_name
   *)
  type interface_dec =
      (*%
       * @format({interfaceId, interfaceName, requiredIds: req reqs,
       *          provideTopdecs: dec decs})
       * "interface" +d interfaceId 2[
       * reqs(req)()
       * +1 "provide" +d interfaceName
       * decs(dec)()
       * +1 "end"
       * ]
       * @format:req({id, loc}) +1 "require" +d id
       * @format:dec(dec) +1 dec
       *)
      {
        interfaceId: InterfaceID.id,
        interfaceName: InterfaceName.interface_name,
        requiredIds: {id: InterfaceID.id, loc: loc} list,
        provideTopdecs: itopdec list
      }

  (*%
   * @formatter(InterfaceID.id) InterfaceID.format_id
   * @formatter(Absyn.topdec) AbsynFormatter.format_topdec
   *)
  type interface =
      (*%
       * @format({interfaceDecs: bind binds, provide})
       * binds(bind)()
       * "provide" +d "interface" 2[ +1 provide ] "end"
       * @format:bind(bind) bind +1
       * @format:provide({requiredIds: req reqs, locallyRequiredIds: lreq lreqs,
       *                  provideTopdecs: dec decs, topdecsInclude: inc incs})
       * reqs(req)()
       * lreqs(lreq)()
       * decs(dec)()
       * incs(inc)()
       * @format:req({id, loc}) +1 "require" +d id
       * @format:lreq({id, loc}) +1 "local" +d "require" +d id
       * @format:dec(dec) +1 dec
       * @format:inc(inc) +1 "include" +d inc
       *)
      {
        interfaceDecs : interface_dec list,
        provide :
          {requiredIds : {id : InterfaceID.id, loc : loc} list,
           locallyRequiredIds : {id : InterfaceID.id, loc : loc} list,
           provideTopdecs : itopdec list,
           topdecsInclude: Absyn.topdec list}
      }

  (*%
   * @formatter(decList) TermFormat.formatDeclList
   * @formatter(Absyn.topdec) AbsynFormatter.format_topdec
   *)
  type compile_unit =
      (*%
       * @format({interface:interface interfaceOpt, topdecsSource: dec decs})
       * "interface"
       * 2[ +1 interfaceOpt(interface) ]
       * +1 "topdecsSource"
       * 2[ decs:decList(dec)(+1, +1) ]
       * +1 "end"
       *)
      {
        interface : interface option,
        topdecsSource : Absyn.topdec list
      }

  (* an interface loaded as if it is "_require"-ed *)
  (*%
   * @formatter(decList) TermFormat.formatDeclList
   * @formatter(Absyn.topdec) AbsynFormatter.format_topdec
   * @formatter(InterfaceID.id) InterfaceID.format_id
   *)
  type interface_unit =
      (*%
       * @format({interfaceDecs: bind binds,
       *          requiredIds: req reqs,
       *          topdecsInclude: inc incs})
       * "interfaces"
       * 2[ binds:decList(bind)(+1, +1) ]
       * +1 "requires"
       * 2[ reqs:decList(req)(+1, +1) ]
       * +1 "topdecsInclude"
       * 2[ incs:decList(inc)(+1, +1) ]
       * +1 "end"
       * @format:req({id, loc}) "require" +d id
       *)
      {
        interfaceDecs : interface_dec list,
        requiredIds : {id: InterfaceID.id, loc: loc} list,
        topdecsInclude : Absyn.topdec list
      }

end
